// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]

package fswatch /* import "github.com/mozilla/mig/modules/fswatch" */

import (
	"encoding/json"
	"fmt"
	"runtime"

	"github.com/mozilla/mig/modules"
)

type module struct {
}

func (m *module) NewRun() modules.Runner {
	return new(run)
}

func init() {
	modules.Register("fswatch", new(module))
}

type run struct {
	Parameters Parameters
	Results    modules.Result
}

func buildResults(e elements, r *modules.Result) (buf []byte, err error) {
	r.Success = true
	r.Elements = e
	r.FoundAnything = true
	buf, err = json.Marshal(r)
	return
}

var logChan chan string
var agentAlertChan chan string
var handlerErrChan chan error
var configChan chan modules.ConfigParams

var alertChan chan Alert

// Alert severities
const (
	_ = iota
	ALERT_CRITICAL
	ALERT_HIGH
	ALERT_MEDIUM
	ALERT_LOW
)

// Create a new alert and send it to channel ch
func newAlert(sev int, f string, a ...interface{}) {
	var sevstr string
	switch sev {
	case ALERT_CRITICAL:
		sevstr = "critical"
	case ALERT_HIGH:
		sevstr = "high"
	case ALERT_MEDIUM:
		sevstr = "medium"
	case ALERT_LOW:
		sevstr = "low"
	default:
		sevstr = "unknown"
	}
	alertChan <- Alert{
		Severity: sevstr,
		Alert:    fmt.Sprintf(f, a...),
	}
}

// An alert generated by the fswatch module
type Alert struct {
	Severity string `json:"severity"` // critical, etc...
	Alert    string `json:"alert"`    // alert text
}

// String convers an alert to a JSON string
func (a Alert) String() string {
	buf, _ := json.Marshal(a)
	return string(buf)
}

func moduleMain() {
	var cfg config

	// Initialize the channel any alerts will come in on from any
	// goroutines we create
	alertChan = make(chan Alert, 16)

	incfg := <-configChan
	buf, err := json.Marshal(incfg.Config)
	if err != nil {
		handlerErrChan <- err
		return
	}
	err = json.Unmarshal(buf, &cfg)
	if err != nil {
		handlerErrChan <- err
		return
	}
	logChan <- "module received configuration"

	go fsWatch(cfg)
	for {
		a := <-alertChan
		agentAlertChan <- a.String()
	}
}

func requestHandler(p interface{}) (ret string) {
	var results modules.Result
	defer func() {
		if e := recover(); e != nil {
			results.Errors = append(results.Errors, fmt.Sprintf("%v", e))
			results.Success = false
			err, _ := json.Marshal(results)
			ret = string(err)
			return
		}
	}()
	e := elements{Ok: true}
	resp, err := buildResults(e, &results)
	if err != nil {
		panic(err)
	}
	return string(resp)
}

type config struct {
	Paths struct {
		Path []string
	}
}

func (r *run) PersistModConfig() interface{} {
	return &config{}
}

func (r *run) RunPersist(in modules.ModuleReader, out modules.ModuleWriter) {
	logChan = make(chan string, 64)
	agentAlertChan = make(chan string, 64)
	regChan := make(chan string, 64)
	handlerErrChan = make(chan error, 64)
	configChan = make(chan modules.ConfigParams, 1)

	go moduleMain()
	l, spec, err := modules.GetPersistListener("fswatch")
	if err != nil {
		handlerErrChan <- err
	} else {
		regChan <- spec
	}
	go modules.HandlePersistRequest(l, requestHandler, handlerErrChan)
	modules.DefaultPersistHandlers(in, out, logChan, handlerErrChan, regChan,
		agentAlertChan, configChan)
}

func (r *run) Run(in modules.ModuleReader) (resStr string) {
	defer func() {
		if e := recover(); e != nil {
			// return error in json
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e))
			r.Results.Success = false
			err, _ := json.Marshal(r.Results)
			resStr = string(err)
			return
		}
	}()
	runtime.GOMAXPROCS(1)
	sockspec, err := modules.ReadPersistInputParameters(in, &r.Parameters)
	if err != nil {
		panic(err)
	}
	err = r.ValidateParameters()
	if err != nil {
		panic(err)
	}
	resStr = modules.SendPersistRequest(r.Parameters, sockspec)
	return
}

func (r *run) ValidateParameters() (err error) {
	return
}

func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var (
		elem elements
	)

	err = result.GetElements(&elem)
	if err != nil {
		panic(err)
	}
	resStr := fmt.Sprintf("ok:%v", elem.Ok)
	prints = append(prints, resStr)
	if !foundOnly {
		for _, we := range result.Errors {
			prints = append(prints, we)
		}
	}
	return
}

type elements struct {
	Ok bool `json:"ok"`
}

type Parameters struct {
}

func newParameters() *Parameters {
	return &Parameters{}
}
